# -*- coding: utf-8 -*-
# Author   : ZhangQing
# Time     : 2025-08-04 23:31
# File     : chat_routes.py
# Project  : codebuddy_craft
# Desc     : 对话接口
# backend/api/chat_routes.py
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Dict, Any, List, Optional, AsyncGenerator
import json
import asyncio
from datetime import datetime

from backend.database.connection import get_db
from backend.core.dependencies import get_current_user
from backend.core.services.llm_service import LLMService
from backend.core.services.project_service import ProjectService
from backend.core.services.file_service import FileService
from backend.models.user import User
from backend.schemas.chat import (
    ChatMessageRequest,
    ChatMessageResponse,
    ChatHistoryResponse,
    ChatSessionResponse,
    StreamingChatRequest
)
from backend.utils.logger import get_logger

# 创建日志记录器
logger = get_logger(__name__)
from backend.utils.rate_limiter import RateLimiter

router = APIRouter(prefix="/api/chat", tags=["智能对话"])

# 初始化服务
llm_service = LLMService()
project_service = ProjectService()
file_service = FileService()
rate_limiter = RateLimiter()


@router.post("/message", response_model=ChatMessageResponse)
async def send_chat_message(
    request: ChatMessageRequest,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """发送聊天消息并获取AI回复"""
    
    try:
        # 速率限制检查
        if not await rate_limiter.check_rate_limit(
            f"chat_{current_user.id}", 
            max_requests=30, 
            window_seconds=60
        ):
            raise HTTPException(
                status_code=429, 
                detail="发送消息太频繁，请稍后再试"
            )
        
        # 验证项目权限
        if request.project_id:
            project = await project_service.get_project(request.project_id, db)
            if not project or project.user_id != current_user.id:
                raise HTTPException(
                    status_code=404,
                    detail="项目不存在或无权限访问"
                )
        
        # 构建上下文信息
        context = await _build_context(
            request, db, current_user.id
        )
        
        # 生成AI回复
        ai_response = await llm_service.generate_chat_response(
            message=request.message,
            context=context,
            conversation_history=request.history or [],
            user_preferences=await _get_user_preferences(current_user.id, db)
        )
        
        # 保存对话记录（异步任务）
        background_tasks.add_task(
            _save_chat_history,
            request.project_id,
            current_user.id,
            request.message,
            ai_response,
            db
        )
        
        return ChatMessageResponse(
            message=ai_response["content"],
            type=ai_response.get("type", "text"),
            metadata=ai_response.get("metadata", {}),
            suggestions=ai_response.get("suggestions", []),
            code_blocks=ai_response.get("code_blocks", []),
            timestamp=datetime.utcnow()
        )
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"聊天消息处理失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="消息处理失败，请稍后重试"
        )


@router.post("/stream")
async def stream_chat_response(
    request: StreamingChatRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """流式聊天响应"""
    
    try:
        # 速率限制检查
        if not await rate_limiter.check_rate_limit(
            f"stream_{current_user.id}", 
            max_requests=10, 
            window_seconds=60
        ):
            raise HTTPException(
                status_code=429, 
                detail="请求太频繁，请稍后再试"
            )
        
        # 验证项目权限
        if request.project_id:
            project = await project_service.get_project(request.project_id, db)
            if not project or project.user_id != current_user.id:
                raise HTTPException(
                    status_code=404,
                    detail="项目不存在或无权限访问"
                )
        
        # 构建上下文
        context = await _build_context(request, db, current_user.id)
        
        # 返回流式响应
        return StreamingResponse(
            _generate_streaming_response(
                request.message,
                context,
                request.history or [],
                current_user.id
            ),
            media_type="text/plain",
            headers={
                "Cache-Control": "no-cache",
                "Connection": "keep-alive",
                "Content-Type": "text/plain; charset=utf-8"
            }
        )
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"流式聊天失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="流式响应失败"
        )


@router.get("/history/{project_id}", response_model=ChatHistoryResponse)
async def get_chat_history(
    project_id: str,
    page: int = 1,
    size: int = 50,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """获取项目聊天历史"""
    
    try:
        # 验证项目权限
        project = await project_service.get_project(project_id, db)
        if not project or project.user_id != current_user.id:
            raise HTTPException(
                status_code=404,
                detail="项目不存在或无权限访问"
            )
        
        # 获取聊天历史
        history = await _get_project_chat_history(
            project_id, current_user.id, page, size, db
        )
        
        return ChatHistoryResponse(
            messages=history["messages"],
            total=history["total"],
            page=page,
            size=size,
            has_more=history["has_more"]
        )
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取聊天历史失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="获取聊天历史失败"
        )


@router.delete("/history/{project_id}")
async def clear_chat_history(
    project_id: str,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """清除项目聊天历史"""
    
    try:
        # 验证项目权限
        project = await project_service.get_project(project_id, db)
        if not project or project.user_id != current_user.id:
            raise HTTPException(
                status_code=404,
                detail="项目不存在或无权限访问"
            )
        
        # 清除聊天历史
        await _clear_project_chat_history(project_id, current_user.id, db)
        
        return {"message": "聊天历史已清除"}
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"清除聊天历史失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="清除聊天历史失败"
        )


@router.get("/sessions", response_model=List[ChatSessionResponse])
async def get_chat_sessions(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """获取用户的聊天会话列表"""
    
    try:
        sessions = await _get_user_chat_sessions(current_user.id, db)
        
        return [
            ChatSessionResponse(
                project_id=session["project_id"],
                project_name=session["project_name"],
                last_message=session["last_message"],
                last_message_time=session["last_message_time"],
                message_count=session["message_count"]
            )
            for session in sessions
        ]
    
    except Exception as e:
        logger.error(f"获取聊天会话失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="获取聊天会话失败"
        )


@router.post("/analyze-code")
async def analyze_code_with_chat(
    request: Dict[str, Any],
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """通过聊天分析代码"""
    
    try:
        code = request.get("code", "")
        language = request.get("language", "python")
        analysis_type = request.get("analysis_type", "general")
        project_id = request.get("project_id")
        
        if not code:
            raise HTTPException(
                status_code=400,
                detail="代码内容不能为空"
            )
        
        # 验证项目权限（如果提供了项目ID）
        if project_id:
            project = await project_service.get_project(project_id, db)
            if not project or project.user_id != current_user.id:
                raise HTTPException(
                    status_code=404,
                    detail="项目不存在或无权限访问"
                )
        
        # 构建分析提示
        analysis_prompt = _build_code_analysis_prompt(
            code, language, analysis_type
        )
        
        # 获取AI分析
        analysis_result = await llm_service.analyze_code_with_context(
            code=code,
            language=language,
            analysis_type=analysis_type,
            prompt=analysis_prompt
        )
        
        return {
            "analysis": analysis_result["content"],
            "suggestions": analysis_result.get("suggestions", []),
            "issues": analysis_result.get("issues", []),
            "improvements": analysis_result.get("improvements", []),
            "complexity_score": analysis_result.get("complexity_score"),
            "metadata": analysis_result.get("metadata", {})
        }
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"代码分析失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="代码分析失败"
        )


@router.post("/explain-code")
async def explain_code_with_chat(
    request: Dict[str, Any],
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """通过聊天解释代码"""
    
    try:
        code = request.get("code", "")
        language = request.get("language", "python")
        detail_level = request.get("detail_level", "medium")
        project_id = request.get("project_id")
        
        if not code:
            raise HTTPException(
                status_code=400,
                detail="代码内容不能为空"
            )
        
        # 验证项目权限
        if project_id:
            project = await project_service.get_project(project_id, db)
            if not project or project.user_id != current_user.id:
                raise HTTPException(
                    status_code=404,
                    detail="项目不存在或无权限访问"
                )
        
        # 获取代码解释
        explanation = await llm_service.explain_code(
            code=code,
            language=language,
            detail_level=detail_level
        )
        
        return {
            "explanation": explanation["content"],
            "key_concepts": explanation.get("key_concepts", []),
            "flow_description": explanation.get("flow_description", ""),
            "complexity_explanation": explanation.get("complexity_explanation", ""),
            "related_topics": explanation.get("related_topics", [])
        }
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"代码解释失败: {str(e)}")
        raise HTTPException(
            status_code=500,
            detail="代码解释失败"
        )


# 辅助函数

async def _build_context(
    request: Any, 
    db: AsyncSession, 
    user_id: str
) -> Dict[str, Any]:
    """构建对话上下文"""
    
    context = {
        "user_id": user_id,
        "timestamp": datetime.utcnow().isoformat(),
        "project_info": None,
        "referenced_files": [],
        "recent_activity": []
    }
    
    # 添加项目信息
    if hasattr(request, 'project_id') and request.project_id:
        try:
            project = await project_service.get_project(request.project_id, db)
            if project:
                context["project_info"] = {
                    "id": project.id,
                    "name": project.name,
                    "description": project.description,
                    "tech_stack": project.tech_stack,
                    "type": project.type
                }
                
                # 获取项目文件概览
                files = await file_service.get_project_files(request.project_id, db)
                context["project_files"] = [
                    {
                        "id": f.id,
                        "path": f.path,
                        "language": f.language,
                        "size": len(f.content or "")
                    }
                    for f in files[:10]  # 限制数量
                ]
        except Exception as e:
            logger.warning(f"获取项目上下文失败: {str(e)}")
    
    # 解析文件引用（@文件名）
    if hasattr(request, 'message'):
        referenced_files = await _parse_file_references(
            request.message, request.project_id, db
        )
        context["referenced_files"] = referenced_files
    
    return context


async def _parse_file_references(
    message: str, 
    project_id: Optional[str], 
    db: AsyncSession
) -> List[Dict[str, Any]]:
    """解析消息中的文件引用"""
    
    referenced_files = []
    
    if not project_id:
        return referenced_files
    
    # 简单的文件引用解析（@文件名）
    import re
    file_references = re.findall(r'@(\S+)', message)
    
    for file_ref in file_references:
        try:
            # 查找匹配的文件
            files = await file_service.search_files_by_path(
                project_id, file_ref, db
            )
            
            for file in files:
                referenced_files.append({
                    "id": file.id,
                    "path": file.path,
                    "content": file.content[:2000] if file.content else "",  # 限制内容长度
                    "language": file.language,
                    "size": len(file.content or "")
                })
        
        except Exception as e:
            logger.warning(f"解析文件引用失败 {file_ref}: {str(e)}")
    
    return referenced_files


async def _get_user_preferences(user_id: str, db: AsyncSession) -> Dict[str, Any]:
    """获取用户偏好设置"""
    
    # 这里可以从数据库获取用户偏好
    # 暂时返回默认设置
    return {
        "preferred_language": "python",
        "code_style": "professional",
        "explanation_level": "detailed",
        "include_comments": True,
        "include_type_hints": True
    }


async def _generate_streaming_response(
    message: str,
    context: Dict[str, Any],
    history: List[Dict[str, Any]],
    user_id: str
) -> AsyncGenerator[str, None]:
    """生成流式响应"""
    
    try:
        # 获取流式响应
        async for chunk in llm_service.generate_streaming_response(
            message=message,
            context=context,
            conversation_history=history
        ):
            # 格式化为SSE格式
            yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
            await asyncio.sleep(0.01)  # 小延迟避免过快
        
        # 发送结束标识
        yield "data: [DONE]\n\n"
    
    except Exception as e:
        error_msg = {"error": f"流式响应错误: {str(e)}"}
        yield f"data: {json.dumps(error_msg, ensure_ascii=False)}\n\n"


async def _save_chat_history(
    project_id: Optional[str],
    user_id: str,
    user_message: str,
    ai_response: Dict[str, Any],
    db: AsyncSession
):
    """保存聊天历史"""
    
    try:
        # 这里应该保存到数据库
        # 为了简化，暂时只记录日志
        logger.info(f"保存聊天记录 - 用户: {user_id}, 项目: {project_id}")
        
        # TODO: 实现实际的数据库保存逻辑
        # chat_record = ChatHistory(
        #     user_id=user_id,
        #     project_id=project_id,
        #     user_message=user_message,
        #     ai_response=ai_response["content"],
        #     metadata=ai_response.get("metadata", {}),
        #     timestamp=datetime.utcnow()
        # )
        # db.add(chat_record)
        # await db.commit()
    
    except Exception as e:
        logger.error(f"保存聊天历史失败: {str(e)}")


async def _get_project_chat_history(
    project_id: str,
    user_id: str,
    page: int,
    size: int,
    db: AsyncSession
) -> Dict[str, Any]:
    """获取项目聊天历史"""
    
    # TODO: 实现实际的数据库查询
    # 暂时返回模拟数据
    return {
        "messages": [],
        "total": 0,
        "has_more": False
    }


async def _clear_project_chat_history(
    project_id: str,
    user_id: str,
    db: AsyncSession
):
    """清除项目聊天历史"""
    
    # TODO: 实现实际的数据库删除
    logger.info(f"清除聊天历史 - 项目: {project_id}, 用户: {user_id}")


async def _get_user_chat_sessions(
    user_id: str,
    db: AsyncSession
) -> List[Dict[str, Any]]:
    """获取用户聊天会话"""
    
    # TODO: 实现实际的数据库查询
    # 暂时返回空列表
    return []


def _build_code_analysis_prompt(
    code: str,
    language: str,
    analysis_type: str
) -> str:
    """构建代码分析提示"""
    
    analysis_prompts = {
        "general": "请分析这段代码的整体质量、结构和潜在问题。",
        "performance": "请分析这段代码的性能特征，找出可能的性能瓶颈和优化建议。",
        "security": "请从安全角度分析这段代码，识别潜在的安全风险和漏洞。",
        "maintainability": "请分析这段代码的可维护性，包括可读性、模块化程度等。",
        "best_practices": "请检查这段代码是否符合最佳实践和编码规范。"
    }
    
    base_prompt = analysis_prompts.get(analysis_type, analysis_prompts["general"])
    
    return f"""
{base_prompt}

代码语言: {language}
代码内容:
```{language}
{code}
请提供:

代码质量评估
发现的问题和改进建议
复杂度分析
具体的修改建议（如果有）
请用中文回答，并提供具体的例子和建议。
"""

# 错误处理和验证装饰器
def validate_message_length(max_length: int = 10000):
    """验证消息长度的装饰器"""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            request = None
            for arg in args:
                if hasattr(arg, 'message'):
                    request = arg
                    break

            if request and len(request.message) > max_length:
                raise HTTPException(
                    status_code=400,
                    detail=f"消息长度不能超过{max_length}字符"
                )
            
            return await func(*args, **kwargs)
        return wrapper
    return decorator

def log_chat_interaction(func):
    """记录聊天交互的装饰器"""
    async def wrapper(*args, **kwargs):
        start_time = datetime.utcnow()
        try:
            result = await func(*args, **kwargs)
            duration = (datetime.utcnow() - start_time).total_seconds()
            logger.info(f"聊天交互完成 - 函数: {func.__name__}, 耗时: {duration:.2f}s")
            return result
        except Exception as e:
            duration = (datetime.utcnow() - start_time).total_seconds()
            logger.error(f"聊天交互失败 - 函数: {func.__name__}, 耗时: {duration:.2f}s, 错误: {str(e)}")
            raise
    return wrapper


